package server;

import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.Date;

import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import authentication.AuthenticationInterface;
import authentication.UserAuthentication;


import clientData.ForumDataInt;
import clientData.MemberDataInt;
import clientData.PostDataInt;
import clientData.TopicDataInt;
import dataHandlers.DataControlerInt;
import dataHandlers.DataFactory;
import exception.ForumNotFoundException;
import exception.FriendNotFoundException;
import exception.LoggedInException;
import exception.NoPermissionException;
import exception.NotLoggedInException;
import exception.NotRegisteredException;
import exception.NotUserPostException;
import exception.PostNotExistException;
import exception.TopicNotFoundException;
import exception.UserNameExistException;

public class ForumHandler  extends java.rmi.server.UnicastRemoteObject implements ForumHandlerInt {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private ConcurrentHashMap<String, LogedOnUser> loggedInUsers;
	private DataControlerInt dc;
	private Logger serverLogger = Logger.getLogger("serverLogger");
	private AuthenticationInterface passwords; 
	private Notifier notifier;

	public ForumHandler() throws java.rmi.RemoteException
	{
		//this.activeTopicsUsers = new ConcurrentHashMap<Integer, HashSet<String>>();
		this.loggedInUsers = new ConcurrentHashMap<String, LogedOnUser>();
		this.dc = DataFactory.getDataControler();
		this.passwords = new UserAuthentication("users.txt");
		this.notifier = new Notifier(this.loggedInUsers,this.dc);
		
		Thread notify = new Thread(notifier);
		notify.start();
		
	}

	private void checkIfLoggedIn(String userName) throws NotLoggedInException{
		
		if (this.loggedInUsers.get(userName)==null) throw new NotLoggedInException();
		Date lastActionDate = this.loggedInUsers.get(userName).getLoggedTime();
		if (lastActionDate==null)
		{
			serverLogger.warning("user:"+ userName +" isn't logged in" );
			throw new NotLoggedInException();
		}
		//after 20 minutes from the user last action, the user is logged off
		boolean isTimedOut = ((double)( new Date().getTime() - lastActionDate.getTime()) / 60000 ) > 20;
		if (isTimedOut){ 
			serverLogger.warning("user:"+ userName +" is logged out because of time out" );
			this.loggedInUsers.remove(userName);
			throw new NotLoggedInException();
		}
		
		this.loggedInUsers.get(userName).setLoggedTime(new Date());
	}
	
	public void addFriend(String userName, String password, String friendUserName)
			throws  NotLoggedInException, FriendNotFoundException, SQLException
	{
		checkIfLoggedIn(userName);
		
		if (!this.dc.userExist(friendUserName))
		{
			serverLogger.warning("user:"+ userName +" try to add friend that doesn't exist in the system" );
			throw new FriendNotFoundException();
		}
		
		
			this.dc.addFriend(userName, friendUserName);
	
	
		if (this.loggedInUsers.containsKey(friendUserName))
			this.loggedInUsers.get(friendUserName).addIntrested(userName);
		
		serverLogger.info("user:"+ userName +" add " + friendUserName + "as friend");
	}

	@Override
	public void createForum(String userName, String password, String forumName)
			throws NotLoggedInException, NoPermissionException, SQLException {
		
		checkIfLoggedIn(userName);
		
		if(!this.passwords.getUserPermission(userName).equals("ADMIN"))
		{
			throw new NoPermissionException(); 
		}
		
		
		
		try{
			this.dc.addForum(userName, forumName);
		}catch(SQLException s){
			throw new SQLException();
		}
		//serverLogger.info("user:"+ userName +" added a new forum named " + forumName);
	}

	
	public void createNewTopicInForum(String userName, String password,
			int forumId, String topicTitle,String postTitle,String postContent) throws NotLoggedInException,ForumNotFoundException, NoPermissionException,java.rmi.RemoteException, SQLException {
		checkIfLoggedIn(userName);
		
		
		if(!this.passwords.getUserPermission(userName).equals("ADMIN"))
		{
			throw new NoPermissionException(); 
		}
		
		
		if(!this.dc.forumExist(forumId))
		{
			serverLogger.warning("user:"+ userName +"try to creat topic but forum id:"+forumId+" does not exist");
			throw new ForumNotFoundException();
		}
				
		
		try{
		int topicId = this.dc.addTopic(userName,forumId,topicTitle);
		
		
		serverLogger.info("user:"+ userName +" added a new topic named " + topicTitle);
			try {
				this.publishPostInTopic(userName, password, topicId, postTitle, forumId, postContent);
			} catch (TopicNotFoundException e) {
				//can't happen
				e.printStackTrace();
			}
		}catch(SQLException s){
			throw new SQLException();
		}
	

		
	}

	@Override
	public LinkedList<ForumDataInt> getForumList(String userName,String password) throws NotLoggedInException, SQLException {
		checkIfLoggedIn(userName);
		serverLogger.info("user:"+ userName +" asked for the list of forums ");
		
		this.loggedInUsers.get(userName).setCurr_forum(-1);
		this.loggedInUsers.get(userName).setCurr_topic(-1);
		
		try{
			return this.dc.getForums();
		}catch(SQLException s){
			throw new SQLException();
		}
	}

	@Override
	public LinkedList<TopicDataInt> getForumTopicList(String userName,
			String password, int forumId) throws NotLoggedInException, ForumNotFoundException, SQLException {
		checkIfLoggedIn(userName);
		
		if(!this.dc.forumExist(forumId))
		{
			serverLogger.warning("user:"+ userName +"try to watch forum topics but forum id:"+forumId+" does not exist");
			throw new ForumNotFoundException();
		}
		
		serverLogger.info("user:"+ userName +" asked for the list of topics. forumid of topics:"+forumId);
		
		this.loggedInUsers.get(userName).setCurr_forum(-1);
		this.loggedInUsers.get(userName).setCurr_topic(-1);
		
		try{
		return this.dc.getTopics(forumId);
		}catch(SQLException s){
			throw new SQLException();
		}
	}

	@Override
	public LinkedList<PostDataInt> getPostList(String userName,
			String password, int topicId, int forumId) throws NotLoggedInException, ForumNotFoundException, TopicNotFoundException, SQLException {
		checkIfLoggedIn(userName);
		
		if(!this.dc.forumExist(forumId))
		{
			serverLogger.warning("user:"+ userName +"try to see posts but forum id:"+forumId+" does not exist");
			throw new ForumNotFoundException();
		}
		
		if(!this.dc.topicExist(forumId, topicId))
		{
			serverLogger.warning("user:"+ userName +"try to see posts but topic id:"+topicId+" does not exist");
			throw new TopicNotFoundException();
		}
		
		
		
		
		this.loggedInUsers.get(userName).setCurr_forum(forumId);
		this.loggedInUsers.get(userName).setCurr_topic(topicId);
		
		serverLogger.info("user:"+ userName +" asked for the list of posts in forum:"+forumId+" in topic:"+topicId);
		try{
		return this.dc.getPosts(forumId, topicId);
		}catch(SQLException s){
			throw new SQLException();
		}
	}



	
	@Override
	public LinkedList<MemberDataInt> getUserFriends(String userName,String password) throws NotLoggedInException, SQLException{
		checkIfLoggedIn(userName);
		serverLogger.info("user:"+ userName +" asked for the list of his friends.");
		try{
			return this.dc.getFriends(userName);
		}
		catch(SQLException s){
			throw new SQLException();
		}
	}

	
	public String login(String userName, String password) throws NotRegisteredException, LoggedInException, SQLException {
		System.out.println("inside loggin");
		
		if (!this.passwords.checkUser(userName, password)){
			serverLogger.info("user:"+ userName +" is not registered.");
			throw new NotRegisteredException();
		}
		
		try {
			checkIfLoggedIn(userName);
			throw new LoggedInException();
		} catch (NotLoggedInException e) 
		{
			
			System.out.println("inside catch");
			this.loggedInUsers.put(userName,new LogedOnUser(new Date()));
			LinkedList<MemberDataInt> userFriendsList = null;
			
			 userFriendsList = this.dc.getFriends(userName);
			//}
			//catch(SQLException h){
			//	throw new SQLException();
			//}
			for (String logUser:this.loggedInUsers.keySet())
			{
				
				LinkedList<MemberDataInt> friendsList = this.dc.getFriends(logUser);
				for (MemberDataInt memData:friendsList)
					if (memData.getUserName().equals(userName))
					{
				//	System.out.println("added to user:"+userName+" the log user:"+logUser );
						this.loggedInUsers.get(userName).addIntrested(logUser);
						break;
					}
				
				for (MemberDataInt memData:userFriendsList)
					if (memData.getUserName().equals(logUser))
					{
					//System.out.println("added to user:"+userName+" the log user:"+logUser );
						this.loggedInUsers.get(logUser).addIntrested(userName);
						break;
					}
					
			}
			
			
			try{
			LinkedList<MemberDataInt> friendsList = this.dc.getFriends(userName);
			
			for (MemberDataInt friendData:friendsList)
				if (this.loggedInUsers.contains(friendData.getUserName()))
					this.loggedInUsers.get(friendData.getUserName()).addIntrested(userName);
			}catch(SQLException s){
				throw new SQLException();
			}
			
			
			
			
			serverLogger.info("user:"+ userName +" has logged in.");
			
			
			System.out.println(this.passwords.getUserPermission(userName));
			return this.passwords.getUserPermission(userName);
		}
	}

	@Override
	public void logout(String userName, String password){
		
		for (LogedOnUser logUser:this.loggedInUsers.values())
			logUser.removeFromIntrested(userName);
		
		this.loggedInUsers.remove(userName);
		serverLogger.info("user:"+ userName +" haslogged out.");
		
	}

	@Override
	public LinkedList<MemberDataInt> lookForForumUsers(String userName, String password, String userNamePrefix) throws NotLoggedInException, SQLException {
		checkIfLoggedIn(userName);
		serverLogger.warning("user:"+ userName +" is looking for forum members with username prefix:"+userNamePrefix);
		try{
		return this.dc.getMembersByPrefix(userNamePrefix);
		}catch(SQLException s){
			throw new SQLException();
		}
	}

	@Override
	public void publishPostInTopic(String userName, String password,int topicId, String postTitle,int forumId, String postContent)
			throws NotLoggedInException, TopicNotFoundException, ForumNotFoundException, SQLException {
		checkIfLoggedIn(userName);
		
		if(!this.dc.forumExist(forumId))
		{
			serverLogger.warning("user:"+ userName +"try to publish new post but forum id:"+forumId+" does not exist");
			throw new ForumNotFoundException();
		}
		
		if(!this.dc.topicExist(forumId, topicId))
		{
			serverLogger.warning("user:"+ userName +"try to publish new post but topic id:"+topicId+" does not exist");
			throw new TopicNotFoundException();
		}
		
		
		
		try{
		int postId = this.dc.addPost(userName, forumId, topicId,postTitle,postContent);
		
		NotificationMsg msg = new NotificationMsg(userName,this.dc.getForumData(forumId),this.dc.getTopicData(forumId, topicId),this.dc.getPostData(forumId, topicId, postId));
		
		this.notifier.addMsg(msg);
		
		serverLogger.info("user:"+ userName +" has publish new post with title:"+postTitle+" in forum:"+forumId+" in topic:"+topicId);
		}catch(SQLException s){
			throw new SQLException();
		}
		}

	@Override
	public void register(String userName, String password, int id, String email, String firstName, String lastName)
			throws SQLException, Exception {
		String permission = "ADMIN"; // change in the future.
		boolean success = this.passwords.addUser(userName, password, permission);
		if(!success){
			serverLogger.warning("user:"+ userName +" already exist.");
			throw new UserNameExistException();
		}
		try{
		this.dc.addMember(userName,id,email,firstName,lastName);
		serverLogger.info("user:"+ userName +" has registered in the forum system");
		}
		catch(SQLException s){
			throw new SQLException();
		}
	}

	@Override
	public void removeFriend(String userName, String password,String friendUserName) 
			throws NotLoggedInException, FriendNotFoundException, SQLException {
		checkIfLoggedIn(userName);
		if (!this.dc.userExist(friendUserName))
		{
			serverLogger.warning("user:"+ userName +" try to remove from his friends' list, friend that doesn't exist." );
			throw new FriendNotFoundException();
		}
		
		if (this.loggedInUsers.containsKey(friendUserName))
			this.loggedInUsers.get(friendUserName).removeFromIntrested(userName);
		try{
		this.dc.removeFriend(userName, friendUserName);
		serverLogger.info("user:"+ userName +" has removed:"+ friendUserName + "from his friends list");
		}catch(SQLException s){
			throw new SQLException();
		}
		}

	public void editPost(String userName, String password,int forumId,int topicId, int postId, String title, String content) 
	throws NotLoggedInException, ForumNotFoundException, TopicNotFoundException, PostNotExistException, NotUserPostException, SQLException{
		System.out.println("user name:"+userName+ ",password: "+password+", forumid:"+forumId+", topicid:"+topicId+", postid:"+postId+", title:"+title+", content:"+content);
		
		checkIfLoggedIn(userName);
		if(!this.dc.forumExist(forumId))
		{
			serverLogger.warning("user:"+ userName +"try to edit post but forum id:"+forumId+" does not exist");
			throw new ForumNotFoundException();
		}
		
		if(!this.dc.topicExist(forumId, topicId))
		{
			serverLogger.warning("user:"+ userName +"try to edit post but topic id:"+topicId+" does not exist");
			throw new TopicNotFoundException();
		}
		
		//System.out.println("before");
		if((this.passwords.getUserPermission(userName).equals("MEMBER"))&&(!this.dc.isCreatorOfPost(forumId, topicId, postId, userName)))
		{
			serverLogger.warning("user:"+ userName +"try to edit post that doesn't belonge to him");
			throw new NotUserPostException();
		}
		//System.out.println("after");
		try{
		this.dc.editPost(postId,forumId,topicId, content, title);
		}catch(SQLException s){
			throw new SQLException();
		}
		
		
		
		NotificationMsg msg = new NotificationMsg(userName,this.dc.getForumData(forumId),this.dc.getTopicData(forumId, topicId),this.dc.getPostData(forumId, topicId, postId));
		
		msg.setType(Notifier.POST_UPDATED);
		
		this.notifier.addMsg(msg);
		
		
		serverLogger.info("user:"+ userName +" has edit post:"+postId+" in forum:"+forumId+" in topic:"+topicId);
	}
	
	@Override
	public String testConnection() throws RemoteException {
		return null;
	}

	public LinkedList<NotificationMsg> getNotificationMsgs(String userName) throws java.rmi.RemoteException, NotLoggedInException{
		checkIfLoggedIn(userName);
		
		LinkedList<NotificationMsg> msgs = new LinkedList<NotificationMsg>();
		
		
		NotificationMsg msg;
		try {
			msg = this.loggedInUsers.get(userName).getMsg().take();
		} catch (InterruptedException e) {
			e.printStackTrace();
			return null;
		}
		
		
		msgs.add(msg);
		
		while((msg = this.loggedInUsers.get(userName).getMsg().poll())!= null)
				msgs.add(msg);
		
		
		for (NotificationMsg nmsg:msgs)
			System.out.println(nmsg.getUserName()+" add new post with title:"+nmsg.getPost().getTitle()+", reason"+nmsg.getType());
		
		return msgs;
		
	}
	
	
	public LinkedList<NotificationMsg> getNotificationMsgsNb(String userName) throws java.rmi.RemoteException, NotLoggedInException{
		checkIfLoggedIn(userName);
		
		LinkedList<NotificationMsg> msgs = new LinkedList<NotificationMsg>();
		
		
		NotificationMsg msg;
		
		
		while((msg = this.loggedInUsers.get(userName).getMsg().poll())!= null)
				msgs.add(msg);
		
		//System.out.println("user:"+userName+" topic:"+ this.loggedInUsers.get(userName).getCurr_topic()+" forum:"+this.loggedInUsers.get(userName).getCurr_forum());
		
	//	for (NotificationMsg nmsg:msgs)
	//		System.out.println(nmsg.getUserName()+" add new post with title:"+nmsg.getPost().getTitle()+", reason"+nmsg.getType());
		
		
		return msgs;
		
	}

	@Override
	public void removePost(String userName, String password, int forumId,
			int topicId, int postId) throws TopicNotFoundException, ForumNotFoundException, NoPermissionException,NoPermissionException, NotLoggedInException, PostNotExistException, SQLException  {
			
		
		System.out.println("un:"+userName+",pass:"+ password+",fid::"+forumId+",tid:"+topicId+",pid:"+postId);
		checkIfLoggedIn(userName);
			
			if(!this.dc.forumExist(forumId))
			{
				serverLogger.warning("user:"+ userName +"try to remove post but forum id:"+forumId+" does not exist");
				throw new ForumNotFoundException();
			}

			if(!this.dc.topicExist(forumId, topicId))
			{
				serverLogger.warning("user:"+ userName +"try to remove post but topic id:"+topicId+" does not exist");
				throw new TopicNotFoundException();
			}
			if(!this.dc.postExist(forumId, topicId, postId))
			{
				serverLogger.warning("user:"+ userName +"try to remove post but post id:"+postId+" does not exist");
				throw new PostNotExistException();
			}
			if((this.passwords.getUserPermission(userName).equals("MEMBER"))&&(!(this.dc.isCreatorOfPost(forumId, topicId, postId, userName))))
			{
				serverLogger.warning("user:"+ userName +"try to remove post but doesnt has moderator premission");
				throw new NoPermissionException();
			}
			
			NotificationMsg msg = new NotificationMsg(userName,this.dc.getForumData(forumId),this.dc.getTopicData(forumId, topicId),this.dc.getPostData(forumId, topicId, postId));
			try{
			this.dc.removePost(forumId, topicId, postId);
			}catch(SQLException s){
				throw new SQLException();
			}

			
			msg.setType(Notifier.POST_DELETED);
			this.notifier.addMsg(msg);

			serverLogger.info("user:"+ userName +" has remove post:"+postId+" from forum:"+forumId+" in topic:"+topicId);
		}

	
	@Override
	public void removeTopic(String userName, String password, int forumId,
			int topicId) {
		// TODO Auto-generated method stub
		
	}
	
	public void removeUser(int pass, String _userName) throws SQLException{
		if(pass==12357911){
			dc.removeMember(_userName);
			passwords.deleteUser(_userName);
		}
	}
	
}
